/*
 * tclsample.c --
 *
 *	This file implements a Tcl interface to the secure hashing
 *	algorithm functions in sha1.c
 *
 * Copyright (c) 1999 Scriptics Corporation.
 * Copyright (c) 2003 ActiveState Corporation.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 */

/*
 * Modified from tclmd5.c by Dave Dykstra, dwd@bell-labs.com, 4/22/97
 */
#include <tcl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "riif.h"
#include "RIIFLexer.h"
#include "RIIFParser.h"

#define TCL_READ_CHUNK_SIZE 4096

static int
Riif_Analyze(
    ClientData clientData,	/* Not used. */
    Tcl_Interp *interp,		/* Current interpreter */
    int objc,				/* Number of arguments */
    Tcl_Obj *const objv[]	/* Argument strings */
    );


/* Indent Function for formatting debug messages */
void
indent( int level ) {
   int i;
   for (i=0;i<level;i++) { printf(" "); };
}

/* Create our own syntax tree (AST) parser
*/
int
AST_Walker ( pANTLR3_BASE_TREE tree, int level )
{
    int n;
    int i;
    int type;
    pANTLR3_BASE_TREE   t;
    pANTLR3_BASE_TREE   arg1;

/*    indent(level); printf("------- Starting to traverse tree --------- \n"); */
    if (tree->children == NULL || tree->children->size(tree->children) == 0) {
       indent(level); /* printf("Tree is empty\n"); */
    }
    if (tree->isNilNode(tree) == ANTLR3_FALSE) {
       type = tree->getType(tree);
       if (type == COMPONENT) { arg1 = tree->children->get(tree->children, 0); indent(level); printf( "CALL: add_componnet %s\n", arg1->toString(arg1)->chars); }
       if (type == CONSTANT)  { arg1 = tree->children->get(tree->children, 0); indent(level); printf( "CALL: add_constant %s\n",  arg1->toString(arg1)->chars); }
       if (type == PARAMETER) { arg1 = tree->children->get(tree->children, 0); indent(level); printf( "CALL: add_parameter %s\n", arg1->toString(arg1)->chars); }
       if (type == FAIL_MODE) { arg1 = tree->children->get(tree->children, 0); indent(level); printf( "CALL: add_fail_mode %s\n", arg1->toString(arg1)->chars); }
       indent(level); printf( "Node: %s of type %d\n", tree->toString(tree)->chars, tree->getType(tree) );
    }
    if (tree->children != NULL) {
       n = tree->children->size(tree->children);
       if (n>0) {
          indent(level); printf("Exploring %0d children\n", n);
          for (i = 0; i < n; i++) {
             t = (pANTLR3_BASE_TREE) tree->children->get(tree->children, i);
             AST_Walker( t, level+3 );
          }
       }
    }
/*    indent(level); printf("------- Finished traversing tree --------- \n"); */
}

/*
 *----------------------------------------------------------------------
 *
 * Sha1 --
 *
 *	 Implements the new Tcl "analyze" command.
 *
 * Results:
 *	A standard Tcl result
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
Riif_Analyze(
    ClientData clientData,	/* Not used. */
    Tcl_Interp *interp,		/* Current interpreter */
    int objc,				/* Number of arguments */
    Tcl_Obj *const objv[]	/* Argument strings */
    )
{

    // Now we declare the ANTLR related local variables we need.
    // Note that unless you are convinced you will never need thread safe
    // versions for your project, then you should always create such things
    // as instance variables for each invocation.
    // -------------------

    // Name of the input file. Note that we always use the abstract type pANTLR3_UINT8
    // for ASCII/8 bit strings - the runtime library guarantees that this will be
    // good on all platforms. This is a general rule - always use the ANTLR3 supplied
    // typedefs for pointers/types/etc.
    //
    pANTLR3_UINT8 fName;

    // The ANTLR3 character input stream, which abstracts the input source such that
    // it is easy to provide input from different sources such as files, or 
    // memory strings.
    //
    // For an ASCII/latin-1 memory string use:
    //	    input = antlr3NewAsciiStringInPlaceStream (stringtouse, (ANTLR3_UINT64) length, NULL);
    //
    // For a UCS2 (16 bit) memory string use:
    //	    input = antlr3NewUCS2StringInPlaceStream (stringtouse, (ANTLR3_UINT64) length, NULL);
    //
    // For input from a file, see code below
    //
    // Note that this is essentially a pointer to a structure containing pointers to functions.
    // You can create your own input stream type (copy one of the existing ones) and override any
    // individual function by installing your own pointer after you have created the standard 
    // version.
    //
    pANTLR3_INPUT_STREAM input;

    // The lexer is of course generated by ANTLR, and so the lexer type is not upper case.
    // The lexer is supplied with a pANTLR3_INPUT_STREAM from whence it consumes its
    // input and generates a token stream as output.
    //
    pRIIFLexer lxr;

    // The token stream is produced by the ANTLR3 generated lexer. Again it is a structure based
    // API/Object, which you can customise and override methods of as you wish. a Token stream is
    // supplied to the generated parser, and you can write your own token stream and pass this in
    // if you wish.
    //
    pANTLR3_COMMON_TOKEN_STREAM tstream;

    // The Lang parser is also generated by ANTLR and accepts a token stream as explained
    // above. The token stream can be any source in fact, so long as it implements the 
    // ANTLR3_TOKEN_SOURCE interface. In this case the parser does not return anything
    // but it can of course specify any kind of return type from the rule you invoke
    // when calling it.
    //
    pRIIFParser psr;

    // The parser produces an AST, which is returned as a member of the return type of
    // the starting rule (any rule can start first of course). This is a generated type
    // based upon the rule we start with.
    //
    RIIFParser_program_return langAST;

    // The tree nodes are managed by a tree adaptor, which doles
    // out the nodes upon request. You can make your own tree types and adaptors
    // and override the built in versions. See runtime source for details and
    // eventually the wiki entry for the C target.
    //
    pANTLR3_COMMON_TREE_NODE_STREAM nodes;

    // Finally, when the parser runs, it will produce an AST that can be traversed by the 
    // the tree parser: c.f. LangDumpDecl.g3t
    // TBD
    // treePsr;

    // Retrieve filename from tcl arguments
    if (objc == 1) {
      interp->result = "You must specify a file name";
      return TCL_ERROR;
    }
    else
    {
      fName=malloc(strlen(Tcl_GetString(objv[1]))+1);
      strcpy((char *)fName, Tcl_GetString(objv[1]));
    }

    // Create the input stream using the supplied file name
    // (Use antlr3AsciiFileStreamNew for UCS2/16bit input).
    //
    input = antlr3FileStreamNew(fName,ANTLR3_ENC_8BIT);

    // The input will be created successfully, providing that there is enough
    // memory and the file exists etc
    //
    if (input == NULL)
    {
      fprintf(stderr, "Unable to open file %s\n", (char *)fName);
      return TCL_ERROR;
    }

    // Our input stream is now open and all set to go, so we can create a new instance of our
    // lexer and set the lexer input to our input stream:
    //  (file | memory | ?) --> inputstream -> lexer --> tokenstream --> parser ( --> treeparser )?
    //
    lxr = RIIFLexerNew(input);
    
    // Need to check for errors
    //
    if ( lxr == NULL )
    {
      fprintf(stderr, "Unable to create the lexer due to malloc() failure1\n");
      return TCL_ERROR;
    }

    // Our lexer is in place, so we can create the token stream from it
    // NB: Nothing happens yet other than the file has been read. We are just 
    // connecting all these things together and they will be invoked when we
    // call the parser rule. ANTLR3_SIZE_HINT can be left at the default usually
    // unless you have a very large token stream/input. Each generated lexer
    // provides a token source interface, which is the second argument to the
    // token stream creator.
    // Note tha even if you implement your own token structure, it will always
    // contain a standard common token within it and this is the pointer that
    // you pass around to everything else. A common token as a pointer within
    // it that should point to your own outer token structure.
    //
    tstream=antlr3CommonTokenStreamSourceNew(ANTLR3_SIZE_HINT, TOKENSOURCE(lxr));
    
    if (tstream == NULL)
    {
      fprintf(stderr, "Out of memory trying to allocate token stream\n");
      return(TCL_ERROR);
    }

    // Finally, now that we have our lexer constructed, we can create the parser
    //
    psr = RIIFParserNew(tstream);
    
    if (psr == NULL) {
      fprintf(stderr, "Out of memory trying to allocate parser\n");
      return TCL_ERROR;
    }

    // We are all ready to go. Though that looked complicated at first glance,
    // I am sure, you will see that in fact most of the code above is dealing
    // with errors and there isn't really that much to do (isn't this always the
    // case in C? ;-).
    //
    // So, we now invoke the parser. All elements of ANTLR3 generated C components
    // as well as the ANTLR C runtime library itself are pseudo objects. This means
    // that they are represented as pointers to structures, which contain any
    // instance data they need, and a set of pointers to other interfaces or
    // 'methods'. Note that in general, these few pointers we have created here are
    // the only things you will ever explicitly free() as everything else is created
    // via factories, that allocate memory efficiently and free() everything they use
    // automatically when you close the parser/lexer/etc.
    //
    // Note that this means only that the methods are always called via the object
    // pointer and the first argument to any method, is a pointer to the structure itself.
    // It also has the side advantage, if you are using an IDE such as VS2005 that can do it
    // that when you type ->, you will see a list of all the methods the object supports.
    //
    langAST = psr->program(psr);


    // If the parser ran correctly, we will have a tree to parse. In general I recommend
    // keeping your own flags as part of the error trapping, but here is how you can
    // work out if there were errors if you are using the generic error messages
    //
    if (psr->pParser->rec->state->errorCount > 0) {
      fprintf(stderr, "The parser returned %d errors, tree walking aborted.\n", psr->pParser->rec->state->errorCount);
    }
    else
    {
      nodes	= antlr3CommonTreeNodeStreamNewTree(langAST.tree, ANTLR3_SIZE_HINT); // sIZE HINT WILL SOON BE DEPRECATED!!
      
      // This is the standard mechanism of returning tcl values
      char *tmps = (char *)Tcl_Alloc(1024*1024);
      strcpy((char *)tmps,langAST.tree->toStringTree(langAST.tree)->chars);
      Tcl_SetResult(interp, tmps, TCL_DYNAMIC);
      
      //printf("Nodes: %s\n", langAST.tree->toStringTree(langAST.tree)->chars);
      // Call our local AST parser
      AST_Walker( langAST.tree, 0 );
      

      // Tree parsers are given a common tree node stream (or your override)
      // TBD
      //treePsr	= LangDumpDeclNew(nodes);

      //treePsr->decl(treePsr);
      nodes   ->free  (nodes);	    nodes	= NULL;
      //treePsr ->free  (treePsr);	    treePsr	= NULL;
    }

    // We did not return anything from this parser rule, so we can finish. It only remains
    // to close down our open objects, in the reverse order we created them
    //
    psr	    ->free  (psr);		psr		= NULL;
    tstream ->free  (tstream);	tstream	= NULL;
    lxr	    ->free  (lxr);	    lxr		= NULL;
    input   ->close (input);	input	= NULL;

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 *
 *	Initialize the new package.  The string "Riif" in the
 *	function name must match the PACKAGE declaration at the top of
 *	configure.in.
 *
 * Results:
 *	A standard Tcl result
 *
 * Side effects:
 *	The Sample package is created.
 *	One new command "analyze" is added to the Tcl interpreter.
 *
 *----------------------------------------------------------------------
 */

int
Riif_Init(Tcl_Interp *interp)
{
    /*
     * This may work with 8.0, but we are using strictly stubs here,
     * which requires 8.1.
     */
    if (Tcl_InitStubs(interp, "8.1", 0) == NULL) 
    {
      return TCL_ERROR;
    }
    if (Tcl_PkgRequire(interp, "Tcl", "8.1", 0) == NULL) {
	return TCL_ERROR;
    }
    if (Tcl_PkgProvide(interp, PACKAGE_NAME, PACKAGE_VERSION) != TCL_OK) {
	return TCL_ERROR;
    }
    Tcl_CreateObjCommand(interp, "analyze", (Tcl_ObjCmdProc *) Riif_Analyze,
	    (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);

    return TCL_OK;
}
